#pragma once

// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

// Material.h : interface of the Material class
//

#include "Node.h"
#include "MaterialEvent.h"
#include "TextureLayer.h"
#include "MaterialParams.h"
class MaterialManager;
class TextureTransform;


// ----------------------------------------------------------------------------
// Class "Material"
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  We make it non-copyable because of the id member variable. We could define
//  a copy ctor. and an op= that set id=-1 for the destination material, but
//  this would probably lead to more confusing client code. So the way it is,
//  in order to copy a material you have to create a new one and copy its 
//  parameters with setParams and getParams
// ----------------------------------------------------------------------------

class Material : public Selectable 
{
public:

    Material();
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    void setId(int newId);
    int getId() const;
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    Color getAmbientColor() const;
    void setAmbientColor(const Color& color);

    Color getDiffuseColor() const;
    void setDiffuseColor(const Color& color);

    void setAmbientAndDiffuseColors(const Color& color);

    Color getSpecularColor() const;
    void setSpecularColor(const Color& color);

    Color getEmissionColor() const;
    void setEmissionColor(const Color& color);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    // 0-1 (maps to OGL's 0-128)
    float getShininess() const;
    void setShininess(float newVal);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    void setTransparent(bool b=true);
    bool isTransparent() const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    size_t getTextureLayerCount() const;
    TextureLayer* getTextureLayer(size_t index) const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    void setPrimaryColorMode(PrimaryColorMode mode);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    void addTextureLayer(ref<Texture> tex, 
                         EnvironmentMode envMode=EnvironmentModes::Modulate,
                         RgbCombinationMode rgbCombiMode=RgbCombinationModes::Modulate,
                         AlphaCombinationMode alphaCombiMode=AlphaCombinationModes::Modulate);

    void addTextureLayerAfter(TextureLayer* position, ref<Texture> tex, 
                         EnvironmentMode envMode=EnvironmentModes::Modulate,
                         RgbCombinationMode rgbCombiMode=RgbCombinationModes::Modulate,
                         AlphaCombinationMode alphaCombiMode=AlphaCombinationModes::Modulate);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    void removeTextureLayer(size_t index);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    MaterialParams getParams() const;
    void setParams(const MaterialParams& newParams);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    // Called by the MaterialManager when the materials is registered
    // with it. Thanks to this function, the relation is two-way

    void setManager(MaterialManager* mm) {  m_manager = mm; }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    void setInstanceContext(const std::vector<ref<TextureTransform> >* instanceContext);

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // Updates the rendering context (typically OpenGL) with the parameters
    // of this material. This should be called prior to rendering any
    // object to which this material is assigned. You should probably call
    // restoreRenderContext when you've finished drawing the current object.
    // See the comments below for additional details

    void setInRenderContext(bool useTextures=true) const;

    // Restores the render context to the state it had before 
    // setRenderInContext was called. You must call ths function as soon as
    // as you have finished whatever object had this material (if you don't,
    // the only consequence is that GL_COLOR_MATERIAL will remain disabled,
    // so it's not really a problem to not calls this function if that flag
    // was disabled beforehand

    void restoreRenderContext();

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // Serialization (Selectable overrides)

    void read(std::wistream& is) override;
    void write(std::wostream& os) const override;

protected:

    friend class TextureLayer;
    void notifyManager(MaterialEvent::Property prop);

private:

    // -----
    // NULL until the Material is registered with a manager
    MaterialManager* m_manager;

    // -----
    // Pointer to avoid having to make a copy (we only need this
    // data while the material is current in the render context)
    const std::vector<ref<TextureTransform> >* m_instanceContext;

    // -----
    int m_id;

    // -----
    MaterialParams m_params;
};
